﻿using Microsoft.Extensions.Logging;
using Nantianmen.Core.Stores;
using Nantianmen.Domain.Models;
using Nantianmen.Shared;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nantianmen.Core.Services
{
    public class RouteService : ServiceBase, IRouteService
    {
        protected virtual CancellationToken CancellationToken { get; } = CancellationToken.None;

        protected virtual ILogger Logger { get; set; }
        protected virtual IRouteStore Store { get; set; }
        protected NantianmenErrorDescriber ErrorDescriber { get; set; }

        public RouteService(ILogger<RouteService> logger, IRouteStore routeStore, NantianmenErrorDescriber errorDescriber)
        {
            Logger = logger;
            Store = routeStore;
            ErrorDescriber = errorDescriber;
        }

        public async Task<NantianmenResult> CreateAsync(Route route)
        {
            ThrowIfDisposed();

            var errors = new List<NantianmenError>();

            var duplicated = await Store.CheckDuplicateRouteIdAsync(route.Id, CancellationToken);

            if (duplicated)
            {
                errors.Add(ErrorDescriber.DuplicateRouteId(route.Id));
                return NantianmenResult.Failed(errors);
            }

            var result = await ValidateRouteAsync(route);
            if (result.Succeeded == false)
            {
                return result;
            }

            return await Store.CreateAsync(route, CancellationToken).ConfigureAwait(false);
        }

        public async Task<NantianmenResult> UpdateAsync(Route route)
        {
            ThrowIfDisposed();

            var result = await ValidateRouteAsync(route).ConfigureAwait(false);

            if(result.Succeeded == false)
            {
                return result;
            }

            return await Store.UpdateAsync(route, CancellationToken).ConfigureAwait(false);
        }

        public async Task<List<Route>> ListRoutesAsync()
        {
            ThrowIfDisposed();

            return await Store.ListRoutesAsync(CancellationToken).ConfigureAwait(false);
        }

        public async Task<Route?> FindByIdAsync(string routeId)
        {
            ThrowIfDisposed();
            ArgumentNullException.ThrowIfNullOrWhiteSpace(routeId);

            return await Store.FindByIdAsync(routeId, CancellationToken).ConfigureAwait(false);
        }

        public async Task<NantianmenResult> RemoveAsync(string routeId)
        {
            ThrowIfDisposed();
            ArgumentNullException.ThrowIfNullOrWhiteSpace(routeId);

            var route = await Store.FindByIdAsync(routeId, CancellationToken).ConfigureAwait(false);

            if(route is null)
            {
                return NantianmenResult.Failed(ErrorDescriber.RouteNotFound(routeId));
            }

            return await Store.RemoveAsync(route, CancellationToken).ConfigureAwait(false);
        }

        private Task<NantianmenResult> ValidateRouteAsync(Route route)
        {
            var errors = new List<NantianmenError>();

            if (string.IsNullOrWhiteSpace(route.Id))
            {
                errors.Add(ErrorDescriber.RouteIdMustNotNullAndNotWhiteSpace());
            }

            return Task.FromResult(errors.Count > 0 ?
                NantianmenResult.Failed(errors) :
                NantianmenResult.Success);
        }
    }
}
